#pragma once

namespace one_rag {
    // basic type lib

    namespace impl {

        template<typename CharT>
        struct RefBuffer
        {
            int refCount;
            int bufSize;
            CharT data[];
        };

        template<typename CharT>
        class String
        {
            const CharT* _data;
            RefBuffer<CharT>* _alloc;
          public:
            //constructors
            String(const CharT*rhs=0);
            String(CharT*rhs);
            String(const String& rhs);
            String(int initialAlloc);
            ~String();
            //operators
            operator bool();
            String& operator=(const String& rhs);
            String& operator=(const CharT* rhs);
            operator const CharT*()const;
            operator CharT*();
            operator bool() const;

            String& operator +=(const CharT); // append a single character
            String& operator +=(const CharT*); // append a string

            // readable functions.
            int Length() const;
            int Size() const;
            // Other operations
            bool Alloc(int cch);
            void Copy(const CharT* src,int srcLen=-1);


            int Compare(const CharT* src) const;
            int Compare(const CharT* src,int srcLen) const;

            // static functions
            static int Length(const CharT* str);
            static void Copy(CharT* dst,const CharT*src);
            static void Copy(CharT* dst,const CharT*src,int cch);
            static int Compare(const CharT*, const CharT*,int cch);

        };
    }
    typedef impl::String<oschar> String;
    typedef impl::String<char> CharString;
    typedef impl::String<wchar_t> WideString;
    typedef impl::String<utf8> Utf8String;
    typedef impl::String<utf16> Utf16String;
    typedef impl::String<utf32> Utf32String;

    namespace impl {

      // default constructor
      // implicit conversion ctor: String("bob");
      // does NOT convert the input string
      template<typename CharT>
      inline String<CharT>::String(const CharT* rhs):_data(rhs),_alloc(0){
      }

      // assignment constructor
      template<typename CharT>
      inline String<CharT>::String(CharT* rhs):_data(0),_alloc(0){
        Copy(rhs);
      }

      // copy constructor
      template<typename CharT>
      inline String<CharT>::String(const String<CharT>& rhs):_data(rhs._data),_alloc(rhs._alloc){
        if(_alloc)
          _alloc->refCount++;
      }

     template<typename CharT>
     inline String<CharT>::String(int initialAlloc):_data(0),_alloc(0){
       Alloc(initialAlloc);
     }


      template<typename CharT>
      inline String<CharT>::~String(){
        if(_alloc){
          if(!--_alloc->refCount)
            free(_alloc);
        }
          _data=0;_alloc=0;
      }

      // assignment operator
      template<typename CharT>
      inline String<CharT>& String<CharT>::operator=(const String& rhs){
        ~String();
        if(this!=&rhs){
          if(_alloc = rhs._alloc)
            _alloc->refCount++;
          _data= rhs._data;
        }
        return *this;
      }


      // assignment operator
      template<typename CharT>
      inline String<CharT>& String<CharT>::operator=(const CharT* rhs){
        if(_alloc && _alloc->refCount > 1){ //tear off the existing buffer
          _alloc->refCount--;
          _alloc=0;
        }
        int cch=Length(rhs);
        Alloc(cch);
        if(cch){
            Copy(_alloc->data,rhs);
        }
        return *this;
      }

      template<typename CharT>
      inline String<CharT>::operator bool() {
        return _data != 0;
      }


      template<typename CharT>
      inline String<CharT>::operator const CharT*()const {
        return _data;
      }

      template<typename CharT>
      inline String<CharT>::operator CharT*(){
        // invoke the assignment operator if _alloc is not exclusive.
        if(!_alloc || _alloc->refCount !=1){
          *this=this->_data;
        }
        return _alloc->data;
      }

      // bool conversion operator.
      template<typename CharT>
      String<CharT>::operator bool() const{
        return _data != 0;
      }


      // append operator
      template<typename CharT>
      String<CharT>& String<CharT>::operator +=(const CharT rhs){
        int cch=Length();
        if(!_alloc || ((_alloc->bufSize - cch) < 1))
          Alloc(cch?cch*2:16);
        _alloc->data[cch++]=rhs;
        _alloc->data[cch]=0;
        return *this;
      }

      // append operator
      template<typename CharT>
      String<CharT>& String<CharT>::operator +=(const CharT* rhs){ // append a string
        int cch1 = Length();
        int cch2 = Length(rhs);
        if(!_alloc || ((_alloc->bufSize < (cch1+cch2+1))))
          Alloc(cch1+cch2);
        Copy(_alloc->data+cch1,rhs);
        return *this;
      }



      // Static Functions
      template<typename CharT>
      inline int String<CharT>::Length(const CharT* str){
        int i=0;
        if(str)
          while(str[i])i++;
        return i;
      }

      template<typename CharT>
      inline void String<CharT>::Copy(CharT* dst,const CharT*src){
        while( ((*dst++)=(*src++)) );
      }

      template<typename CharT>
      inline void String<CharT>::Copy(CharT* dst,const CharT*src,int cch) {
        while(cch--){
          (*dst++)=(*src++);
        }
        *dst = 0;
      }



      template<typename CharT>
      inline int String<CharT>::Length() const{
        return Length(_data);
      }

      template<typename CharT>
      inline int String<CharT>::Size() const{
        return _alloc?_alloc.bufSize:0;
      }

      template<typename CharT>
      inline bool String<CharT>::Alloc(int cch){
        if(!cch){
          free(_alloc);
          _alloc=0;_data=0;
        }
        else{
          RefBuffer<CharT>* t = (RefBuffer<CharT>*)realloc(_alloc,sizeof(RefBuffer<CharT>) + (cch+1)*sizeof(CharT) );
          if(t){
            if(!_alloc)
              t->data[0]=0;
            _alloc = t;
            _alloc->refCount=1;
            _alloc->bufSize=cch+1;
            _data=_alloc->data;
          }
        }
        return _alloc!=0;
      }

      template<typename CharT>
      inline void String<CharT>::Copy(const CharT* source,int cch){
        if(cch == -1)
          cch = Length(source);
        if(Alloc(cch))
          Copy(_alloc->data,source,_alloc->bufSize-1);
      }

      template<typename CharT>
      inline int String<CharT>::Compare(const CharT* src) const
      {
        CharT* scan;
        if( (scan = _data) && src){
          while(*scan && *scan++==*src++);
          return *scan-*src;
        }
        else
          return scan-src;
      }

      // This is not a substring compare. The full rhs string is compared. The strings are not equal if any character is not equal withint srcLen, and srcLen != the length of the string
      template<typename CharT>
      inline int String<CharT>::Compare(const CharT* src,int srcLen) const
      {
        int i;
        if(!src || !_data)
          return _data - src;

        int ret;
        for(i=0;i<srcLen && _data[i]; i++){
          if((ret=(_data[i]-src[i])))
             return ret;
        }
        return _data[i];
      }


    }

 }
